using System;
using System.Drawing;
using System.ComponentModel;
using System.Runtime.Serialization;

using SharpGL.Media3D;
using SharpGL.SceneGraph.Transformations;

namespace SharpGL.SceneGraph.Cameras
{
	/// <summary>
	/// The ArcBallTransformation camera supports arcball projection, making it ideal for use with a mouse.
	/// </summary>
	[DataContract()]
	public class ArcBallCamera : LookAtCamera
    {
        #region Private Fields
        
        /// <summary>
        /// The arcball used for rotating.
        /// </summary>
        [DataMember()]
        private ArcBallTransformation arcBallTransformation = new ArcBallTransformation();

        #endregion Private Fields

        #region Private Methods

        private void OnArcBallTransformationPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            NotifyPropertyChanged(sender.GetType() + "." + e.PropertyName);
        }

        #endregion Private Methods

        #region Constructors
        
        /// <summary>
        /// Initializes a new instance of the <see cref="PerspectiveCamera"/> class.
        /// </summary>
        public ArcBallCamera()
		{
			Name = "Camera (ArcBallTransformation)";
            arcBallTransformation.PropertyChanged += new PropertyChangedEventHandler(OnArcBallTransformationPropertyChanged);
		}

        #endregion Constructors

        #region Public Methods
        
        /// <summary>
        /// This is the class' main function, to override this function and perform a 
		/// perspective transformation.
		/// </summary>
        public override void TransformProjectionMatrix(OpenGL gl)
        {
            if (OpenGL.IsValidContext(gl))
            {
                int[] viewport = new int[4];
                gl.GetInteger(OpenGL.GL_VIEWPORT, viewport);

                //  Perform the perspective transformation.
                arcBallTransformation.SetBounds(new Size(viewport[2], viewport[3]));
                gl.Perspective(FieldOfView, AspectRatio, Near, Far);
                //Point3D target = new Point3D(0, 0, 0);
                //Vector3D upVector = new Vector3D(0, 0, 1);

                //  Perform the look at transformation.
                gl.LookAt((double)Position.X, (double)Position.Y, (double)Position.Z,
                    (double)Target.X, (double)Target.Y, (double)Target.Z,
                    (double)UpVector.X, (double)UpVector.Y, (double)UpVector.Z);

                arcBallTransformation.Transform(gl);
            }
		}

        #endregion Public Methods

        #region Public Properties

        /// <summary>
        /// Gets the arc ball.
        /// </summary>
        public ArcBallTransformation ArcBallTransformation
        {
            get { return arcBallTransformation; }
            set
            {
                if (arcBallTransformation != value)
                {
                    arcBallTransformation.PropertyChanged -= new PropertyChangedEventHandler(OnArcBallTransformationPropertyChanged);

                    if (value == null)
                        arcBallTransformation = new ArcBallTransformation();
                    else
                        arcBallTransformation = value;

                    arcBallTransformation.PropertyChanged += new PropertyChangedEventHandler(OnArcBallTransformationPropertyChanged);

                    NotifyPropertyChanged("ArcBallTransformation");
                }
            }
        }

        #endregion Public Properties
    }
}
